/*
 * Copyright (c) 2020-2099 the original author or authors. All rights reserve.
 */
package org.tianyun.cloud.security.autoconfigure;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.tianyun.cloud.data.redis.factory.ObjectRedisTemplate;
import org.tianyun.cloud.security.configuration.SecurityProperties;
import org.tianyun.cloud.security.configuration.SecurityProperties.YamlTokenStrategy;
import org.tianyun.cloud.security.server.servlet.ServletSecurityFilter;
import org.tianyun.cloud.security.server.servlet.ServletSecurityFilterProxy;
import org.tianyun.cloud.security.server.spring.GatewaySecurityFilter;
import org.tianyun.cloud.security.server.spring.GatewaySecurityFilterProxy;
import org.tianyun.cloud.security.token.enhance.NoTokenSecretEnhanceAdapter;
import org.tianyun.cloud.security.token.enhance.TokenSecretEnhance;
import org.tianyun.cloud.security.token.store.InMemoryTokenStore;
import org.tianyun.cloud.security.token.store.RedisTokenStore;
import org.tianyun.cloud.security.token.store.TokenStore;
import org.tianyun.cloud.security.token.strategy.JwtTokenStrategy;
import org.tianyun.cloud.security.token.strategy.StandardTokenStrategy;
import org.tianyun.cloud.security.token.strategy.TokenStrategy;

import java.util.List;

/**
 * 安全自动配置类
 *
 * @auther ebert_chan
 */
@Configuration(proxyBeanMethods = false)
public class SecurityAutoConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "security", ignoreInvalidFields = true)
    @RefreshScope
    public SecurityProperties securityProperties() {
        return new SecurityProperties();
    }

    @Conditional(TokenStrategyAnyCondition.class)
    class TokenStrategyConfiguration {

        @Bean
        @ConditionalOnMissingBean(TokenSecretEnhance.class)
        public TokenSecretEnhance tokenSecretEnhance() {
            return new NoTokenSecretEnhanceAdapter();
        }

        @ConditionalOnClass(value = ObjectRedisTemplate.class)
        @ConditionalOnProperty(name = "security.token-strategy.token-store", havingValue = "REDIS")
        class RedisTokenStoreConfiguration {

            @Bean
            public TokenStore redisTokenStore(SecurityProperties securityProperties, ObjectRedisTemplate objectRedisTemplate) {
                return new RedisTokenStore(objectRedisTemplate);
            }

        }

        @Bean
        @ConditionalOnMissingBean(TokenStore.class)
        public TokenStore inMemoryTokenStore(SecurityProperties securityProperties) {
            return new InMemoryTokenStore();
        }

        @Bean
        @RefreshScope
        public TokenStrategy tokenStrategy(SecurityProperties securityProperties, TokenStore tokenStore) {
            YamlTokenStrategy tokenStrategy = securityProperties.getTokenStrategy();
            if (tokenStrategy.getJwt() != null) {
                return new JwtTokenStrategy(securityProperties);
            }
            return new StandardTokenStrategy(securityProperties, tokenStore);
        }

    }

    static class TokenStrategyAnyCondition extends AnyNestedCondition {

        public TokenStrategyAnyCondition() {
            super(ConfigurationPhase.PARSE_CONFIGURATION);
        }

        @ConditionalOnProperty(prefix = "security.token-strategy.standard", name = "key")
        class StandardCondition {

        }

        @ConditionalOnProperty(prefix = "security.token-strategy.jwt", name = "secret")
        class JwtCondition {

        }

    }

    @Bean
    @ConditionalOnClass(name = "org.springframework.cloud.gateway.filter.GatewayFilter")
    @ConditionalOnProperty(name = "security.enabled", havingValue = "true")
    public GatewaySecurityFilterProxy gatewaySecurityFilterProxy(SecurityProperties securityProperties, ObjectProvider<List<GatewaySecurityFilter>> gatewaySecurityFilters) {
        return new GatewaySecurityFilterProxy(securityProperties, gatewaySecurityFilters.getIfAvailable());
    }

    @Bean
    @ConditionalOnMissingClass(value = "org.springframework.cloud.gateway.filter.GatewayFilter")
    @ConditionalOnProperty(name = "security.enabled", havingValue = "true")
    public ServletSecurityFilterProxy servletSecurityFilterProxy(SecurityProperties securityProperties, ObjectProvider<List<ServletSecurityFilter>> servletSecurityFilters) {
        return new ServletSecurityFilterProxy(securityProperties, servletSecurityFilters.getIfAvailable());
    }

}
